use anchor_lang::{AnchorDeserialize, AnchorSerialize};
use serde_json::json;
use solana_client::nonblocking::rpc_client::RpcClient;

use solana_sdk::pubkey::Pubkey;
use solana_transaction_status::{UiParsedMessage, UiTransactionStatusMeta};
use std::str::FromStr;

#[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize)]
struct ActionHouse {
    account_discriminator: [u8; 8],
    auction_house_fee_account: [u8; 32],
    auction_house_treasury: [u8; 32],
    treasury_withdrawal_destination: [u8; 32],
    fee_withdrawal_destination: [u8; 32],
    treasury_mint: [u8; 32],
    authority: [u8; 32],
    creator: [u8; 32],
    bump: u8,
    treasury_bump: u8,
    fee_payer_bump: u8,
    seller_fee_basis_points: u16,
    requires_sign_off: bool,
    can_change_sale_price: bool,
    escrow_payment_bump: u8,
    has_auctioneer: bool,
    auctioneer_address: [u8; 32],
    scopes: [bool; 8],
}

#[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize, PartialEq)]
struct AuctionData {
    trade_state_bump: u8,
    free_trade_state_bump: u8,
    program_as_signer_bump: u8,
    buyer_price: u64,
    token_size: u64,
}

pub async fn process_coralcube<'r>(
    rpc_client: &RpcClient,
    _signature: &str,
    message: &UiParsedMessage,
    _meta: &UiTransactionStatusMeta,
) -> Option<serde_json::Value> {
    match &message.instructions[0] {
        solana_transaction_status::UiInstruction::Compiled(_) => None,
        solana_transaction_status::UiInstruction::Parsed(body) => match body {
            solana_transaction_status::UiParsedInstruction::Parsed(_) => None,
            solana_transaction_status::UiParsedInstruction::PartiallyDecoded(body) => {
                if body.accounts.len() >= 5 {
                    let data: &[u8] = &bs58::decode(&body.data).into_vec().unwrap() as &[u8];

                    let account_data = match rpc_client
                        .get_account_data(&Pubkey::from_str(&body.accounts[4]).unwrap())
                        .await
                    {
                        Ok(account_data) => account_data,
                        Err(_) => return None,
                    };

                    let mut account_data = &account_data[..];

                    let auction_house = match ActionHouse::deserialize(&mut account_data) {
                        Ok(action_house) => action_house,
                        Err(_) => return None,
                    };

                    let treasury_mint = Pubkey::new(&auction_house.treasury_mint);
                    let authority = Pubkey::new(&auction_house.authority);

                    let token_account = &body.accounts[1];

                    let account = match rpc_client
                        .get_token_account(&Pubkey::from_str(token_account).unwrap())
                        .await
                    {
                        Ok(account) => match account {
                            Some(account) => account,
                            None => return None,
                        },
                        Err(_) => return None,
                    };

                    let mint = account.mint;

                    let mut data = &data[8..data.len()];

                    let auction_data = match AuctionData::deserialize(&mut data) {
                        Ok(auction_data) => auction_data,
                        Err(_) => return None,
                    };

                    Some(json!({
                        "marketplace": "coralcube",
                        "price": auction_data.buyer_price,
                        "mint": mint,
                        "signer": body.accounts[0],
                        "token": token_account,
                        "treasury_mint": treasury_mint.to_string(),
                        "authority": authority.to_string(),
                        "auction_house": &body.accounts[4],
                    }))
                } else {
                    None
                }
            }
        },
    }
}
